home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C++ / Code Resources / 3D Buttons CDEF 1.0b4 / Source / 3D Buttons CDEF source / (3D Buttons CDEF.π) / LGBPushButton.cp < prev    next >
Encoding:
Text File  |  1994-07-31  |  16.3 KB  |  596 lines  |  [TEXT/MMCC]

  1. /**************************************************************************
  2.     LGBPushButton
  3.     
  4.     Public domain, by Zig Zichterman.
  5.     
  6.     This class implements 3D push buttons according to the guidelines
  7.     suggested in _develop_ 15. Some of the drawing code is taken from
  8.     the public domain source accompanying _develop_ 15.
  9.     
  10.         07/31/94    zz    Draw 3D if 4-bit grey, BW if 4-bit color
  11.         07/31/94    zz    save/restore pen colors
  12.     1.0b3
  13.         07/28/94    zz    use offscreen drawing for crisper feel
  14.         07/20/94    zz    call PenNormal() before drawing anything!
  15.     1.0b1
  16. **************************************************************************/
  17. #include "LGBPushButton.h"
  18.  
  19. #include <GestaltEqu.h>
  20.  
  21. #include "LGBControl.h"
  22. #include "LGBDeviceIterator.h"
  23. #include "UGBDraw.h"
  24.  
  25. const short    LGBPushButton_radius        = 10;
  26. const short LGBPushButton_innerRadius    = 8;
  27.  
  28. /**************************************************************************
  29.     Main()                                                        [static]
  30.     
  31.     Main entry point for all push button calls. Dispatch according to
  32.     message.
  33. **************************************************************************/
  34. long
  35. LGBPushButton::Main(short inVariation, ControlHandle ioControl,
  36.         short inMsg, long ioParam)
  37. {
  38.     long            returnMe    = 0;
  39.  
  40.     // lock the control handle for the duration of this call
  41.     char state = ::HGetState((Handle) ioControl);
  42.     ::HLock((Handle) ioControl);
  43.     
  44.     LGBPushButton    button(*ioControl,(inVariation & useWFont)?true:false);
  45.     
  46.     switch (inMsg) {
  47.         case drawCntl    :
  48.                 button.Draw(ioParam);
  49.                 break;
  50.                             
  51.         case testCntl    :
  52.             {
  53.                 Point    hitPt;
  54.                 hitPt.h    = LoWord(ioParam);
  55.                 hitPt.v    = HiWord(ioParam);
  56.                 if (button.Test(hitPt)) {
  57.                     returnMe = inButton;
  58.                 }
  59.             }
  60.             break;
  61.         
  62.         case calcCRgns    :    // only called in 24-bit mode
  63.             {    // is 32-bit addressing off?
  64.                 long    result    = 0;
  65.                 OSErr    err = ::Gestalt(gestaltAddressingModeAttr,&result);
  66.                 if (!err && ((result &
  67.                         (1L << gestalt32BitAddressing)) == 0)) {
  68.                     RgnHandle    rgn = (RgnHandle)
  69.                                     ::StripAddress((Ptr) ioParam);
  70.                     button.CalcCRgn(rgn);
  71.                 }
  72.             }
  73.             break;
  74.         
  75.         case calcCntlRgn    :    // only called in 32-bit mode
  76.             {
  77.                 button.CalcCRgn((RgnHandle) ioParam);
  78.             }    
  79.             break;
  80.     }
  81.     
  82.     // unlock handle
  83.     ::HSetState((Handle) ioControl,state);
  84.     return returnMe;
  85. }
  86.  
  87. //—————————————————————————————————————————————————————————————————————————
  88. // Constructor
  89. //—————————————————————————————————————————————————————————————————————————
  90.  
  91. /**************************************************************************
  92.     LGBControl(ControlHandle,Boolean)
  93.     
  94.     construct/initialize a control object. Just stores the control handle
  95.     and the "should I use the window font?" flag in data members.
  96. **************************************************************************/
  97. LGBPushButton::LGBPushButton(ControlRecord *inControl, Boolean inUseWFont)
  98.     : mControl(inControl), mUseWFont(inUseWFont)
  99. {
  100.  
  101. }
  102.  
  103. //—————————————————————————————————————————————————————————————————————————
  104. // Dispatch entry points
  105. //—————————————————————————————————————————————————————————————————————————
  106.  
  107. /**************************************************************************
  108.     Draw()
  109.     
  110.     Draw the control. inPartCode is a part code specifying which part of
  111.     the control to draw, or 0 for the entire control.
  112.     
  113.     PushButtons only have inButton, so we ignore the inPartCode.
  114.     
  115.     Save the current drawing environment. Iterate through all the devices
  116.     (screens), drawing the control in color or black and white depending
  117.     on the screen depth. Once done, restore the drawing environment and
  118.     return.
  119. **************************************************************************/
  120. void
  121. LGBPushButton::Draw(long inPartCode)
  122. {
  123.     // if we're invisible, don't draw
  124.     if (mControl->contrlVis == false) return;
  125.     
  126.     // save the font
  127.     short    font,size,mode,face;
  128.     {
  129.         GrafPtr    port;
  130.         ::GetPort(&port);
  131.         font    = port->txFont;
  132.         size    = port->txSize;
  133.         mode    = port->txMode;
  134.         face    = port->txFace;
  135.     }
  136.  
  137.     // save the pen colors        // will probably crash SEs, so test first
  138.     RGBColor    fore,back;
  139.     if (UGBDraw::ColorQDIsPresent()) {
  140.         ::GetForeColor(&fore);
  141.         ::GetBackColor(&back);
  142.     }
  143.     
  144.     // save the clip region
  145.     RgnHandle    saveClip    = ::NewRgn();
  146.     if (!saveClip) return;
  147.     ::GetClip(saveClip);
  148.     
  149.     // make the pen something sensible
  150.     ::PenNormal();
  151.     ::ForeColor(blackColor);
  152.     ::BackColor(whiteColor);
  153.     
  154.     // loop through all the devices (screens)
  155.     UGBDraw::Offscreen    offscreen;
  156.     LGBDeviceIterator    device;
  157.     device.Init(mControl->contrlRect);
  158.     short    depth    = 0;
  159.     do {
  160.         depth = device.Next();
  161.         if (depth == 0) break;    // all done with devices
  162.         UGBDraw::OffscreenPre(offscreen);
  163.         if ((depth < 4)
  164.                 || (depth == 4 && device.mDeviceIsColor)) {
  165.             DrawBW();
  166.         } else {
  167.             
  168.             // erase the the entire background in grey, so that we
  169.             // get the 4 corners, but only if we're drawing offscreen.
  170.             // Too flashy for onscreen drawing, but mandatory for
  171.             // offscreen
  172.             if (offscreen.gworld) {
  173.                 UGBDraw::PenNormal();
  174.                 ::EraseRect(&mControl->contrlRect);
  175.             }
  176.             DrawColor();
  177.         }
  178.         UGBDraw::OffscreenPost(offscreen);
  179.     } while(true);
  180.  
  181.     // restore the clip region
  182.     ::SetClip(saveClip);
  183.     ::DisposeRgn(saveClip);
  184.     
  185.     // restore the pen
  186.     if (UGBDraw::ColorQDIsPresent()) {
  187.         ::RGBForeColor(&fore);
  188.         ::RGBBackColor(&back);
  189.     }
  190.  
  191.     // restore the font
  192.     ::TextFont(font);
  193.     ::TextSize(size);
  194.     ::TextMode(mode);
  195.     ::TextFace(face);
  196. }
  197.  
  198. /**************************************************************************
  199.     Test()
  200.     
  201.     Return inButton if the point is in our rect. If you play with the
  202.     system button definition, it looks like the button tests true even
  203.     for points outside the button's round rect, as long as the points
  204.     are in the button's (non-round) rect.
  205. **************************************************************************/
  206. Boolean
  207. LGBPushButton::Test(Point inHitPt)
  208. {
  209.     return ::PtInRect(inHitPt,&(mControl->contrlRect));
  210. }
  211.  
  212. /**************************************************************************
  213.     CalcCRgn()
  214.     
  215.     Calculate the control's region in the given region handle
  216. **************************************************************************/
  217. void
  218. LGBPushButton::CalcCRgn(RgnHandle ioRgn)
  219. {
  220.     if (!ioRgn) return;        // idiot resistance
  221.     ::OpenRgn();
  222.     ::FrameRoundRect(&(mControl->contrlRect),
  223.             LGBPushButton_radius,LGBPushButton_radius);
  224.     ::CloseRgn(ioRgn);
  225. }
  226.  
  227. //—————————————————————————————————————————————————————————————————————————
  228. // Draw
  229. //—————————————————————————————————————————————————————————————————————————
  230.  
  231. /**************************************************************************
  232.     DrawBW()
  233.     
  234.     Draw the control in black and white. It is NOT safe to make any
  235.     color QuickDraw calls here.
  236. **************************************************************************/
  237. void
  238. LGBPushButton::DrawBW(void)
  239. {
  240.     // Set the pen to black, 1x1
  241.     ::PenNormal();
  242.  
  243.     // erase the insides
  244.     EraseInsides();
  245.     
  246.     // draw the frame
  247.     DrawFrame();
  248.     
  249.     // draw the name
  250.     const short hilite = mControl->contrlHilite;
  251.     DrawName(hilite == 255);
  252.  
  253.     // draw highlight if necessary
  254.     if (hilite && (hilite != 255)) {
  255.         Rect    invertMe    = mControl->contrlRect;
  256.         ::InsetRect(&invertMe,1,1);
  257.         ::InvertRoundRect(&invertMe,
  258.                 LGBPushButton_innerRadius,LGBPushButton_innerRadius);
  259.     }
  260. }
  261.  
  262. /**************************************************************************
  263.     DrawName()
  264.     
  265.     Draw the name. Any color stuff must already be set up by the caller.
  266.     Pass in TRUE if for inDim1bit if you DrawName() to grey
  267.     out the name for you (by applying a 50% dither).
  268. **************************************************************************/
  269. void
  270. LGBPushButton::DrawName(Boolean inDim1Bit)
  271. {
  272.     // draw the name
  273.     if (!mUseWFont) LGBControl::SetupFont();
  274.     
  275.     // calc the max bounds we'll be drawing into. We'll change
  276.     // the vertical bounds later, but we need a starting point
  277.     // for the clip setup below.
  278.     Rect    textRect    = mControl->contrlRect;
  279.     textRect.left++;
  280.     textRect.top++;
  281.     textRect.right    -= 2;
  282.     textRect.bottom--;
  283.     
  284.     // grab faster access to the title
  285.     StringPtr    title    = mControl->contrlTitle;
  286.     
  287.     {    // center vertically
  288.         const short    titleHeight    = LGBControl::TitleHeight(title);
  289.         short    centerV        = (textRect.top + textRect.bottom)/2;
  290.         textRect.top        = centerV - (titleHeight/2);
  291.         textRect.bottom        = textRect.top + titleHeight;
  292.     }
  293.     
  294.     // clip to the insides of the button frame
  295.     RgnHandle    tempClip    = ::NewRgn();
  296.     RgnHandle    currClip    = ::NewRgn();
  297.     if (tempClip && currClip) {
  298.         // build up a region that we'd like to clip to
  299.         ::OpenRgn();
  300.         ::FrameRoundRect(&textRect,LGBPushButton_innerRadius,
  301.                 LGBPushButton_innerRadius);
  302.         ::CloseRgn(tempClip);
  303.         // get the current clip region
  304.         ::GetClip(currClip);
  305.         // find the intersection of the current and desired clip region,
  306.         // set the clip region to that intersection
  307.         ::SectRgn(tempClip,currClip,tempClip);
  308.         ::SetClip(tempClip);
  309.         ::DisposeRgn(tempClip);
  310.     
  311.         // draw the title. We use TextBox() to support
  312.         // multiline titles: DrawString() no habla '\r'
  313.         ::TextBox(title + 1,*title,&textRect,teCenter);
  314.     
  315.         // grey out the name if inactive
  316.         if (inDim1Bit) {
  317.             // fabricate a 50% grey dither through code.
  318.             // this way we don't rely on QuickDraw globals
  319.             unsigned char grey[8];
  320.             grey[0] = grey[2] = grey[4] = grey[6] = 0xAA;
  321.             grey[1] = grey[3] = grey[5] = grey[7] = 0x55;
  322.             ::PenPat((PatPtr) grey);
  323.             ::PenMode(patBic);
  324.             ::PaintRect(&textRect);
  325.             ::PenNormal();
  326.         }
  327.         
  328.         // restore the clip region
  329.         ::SetClip(currClip);
  330.         ::DisposeRgn(currClip);
  331.     }
  332. }
  333.  
  334. /**************************************************************************
  335.     DrawColor()
  336.     
  337.     Draw the control in color
  338. **************************************************************************/
  339. void
  340. LGBPushButton::DrawColor(void)
  341. {
  342.     // active buttons draw 3D, inactive ones draw flat but grey
  343.     if (mControl->contrlHilite == 255) {
  344.         DrawColorInactive();
  345.     } else {
  346.         DrawColorActive();
  347.     }
  348.     
  349.     // restore the pen
  350.     ::PenNormal();
  351.     UGBDraw::PenReallyNormal();
  352. }
  353.  
  354. /**************************************************************************
  355.     DrawColorInactive()
  356.     
  357.     Draw the control inactive, in color. Ignore the control's
  358.     color table, just draw in grey
  359. **************************************************************************/
  360. void
  361. LGBPushButton::DrawColorInactive(void)
  362. {
  363.     // draw frame
  364.     UGBDraw::PenFrameInactive();
  365.     DrawFrame();
  366.  
  367.     // erase background
  368.     UGBDraw::Background();
  369.     EraseInsides();
  370.     
  371.     // draw text. Rely on the side effects of erase backround
  372.     // leaving the fore and back color
  373.     DrawName();
  374.     
  375.     UGBDraw::PenNormal();
  376. }
  377.  
  378. /**************************************************************************
  379.     DrawColorActive()
  380.     
  381.     Draw the control in color, active. Ignore the color table
  382.     except for any tinge colors
  383.     
  384.     ### zz 07/04/94 tinge colors not implemented yet, maybe never
  385. **************************************************************************/
  386. void
  387. LGBPushButton::DrawColorActive(void)
  388. {
  389.     // erase the background
  390.     if (mControl->contrlHilite) {
  391.         UGBDraw::BackGrey(UGBDraw_hiliteBackground);
  392.     } else {
  393.         UGBDraw::Background();
  394.     }
  395.     EraseInsides();
  396.     
  397.     // draw the frame
  398.     UGBDraw::PenFrameActive();
  399.     DrawFrame();
  400.  
  401.     // draw the name
  402.     DrawName();
  403.     
  404.     // draw the 3D effect
  405.     Draw3DEffects();
  406. }
  407.  
  408. /**************************************************************************
  409.     DrawFrame()
  410.     
  411.     Draw the round rect for our button. No 3D effects, just a round rect.
  412.     Caller should set up pen before this call
  413. **************************************************************************/
  414. void
  415. LGBPushButton::DrawFrame(void)
  416. {
  417.     ::FrameRoundRect(&(mControl->contrlRect),
  418.             LGBPushButton_radius,LGBPushButton_radius);
  419. }
  420.  
  421. /**************************************************************************
  422.     EraseInsides()
  423.     
  424.     Erase the insides of the button. Caller should set up the pen
  425. **************************************************************************/
  426. void
  427. LGBPushButton::EraseInsides(void)
  428. {    // erase the insides
  429.     Rect    eraseMe    = mControl->contrlRect;
  430.     ::InsetRect(&eraseMe,1,1);
  431.     ::EraseRoundRect(&eraseMe,
  432.             LGBPushButton_innerRadius,LGBPushButton_innerRadius);
  433. }
  434.  
  435. /**************************************************************************
  436.     Draw3DEffects()
  437.     
  438.     Draw the spiffy 3D effects.
  439. **************************************************************************/
  440. void
  441. LGBPushButton::Draw3DEffects(void)
  442. {
  443.     const Boolean    hilite = (mControl->contrlHilite != 0);
  444.     
  445.     if (hilite) {    // draw highlighted
  446.         {    // outer rim
  447.             Rect    outer    = mControl->contrlRect;
  448.             ::InsetRect(&outer,1,1);
  449.             
  450.             // outside edge, top left shadow
  451.             UGBDraw::ForeGrey(UGBDraw_grey4);
  452.             ::MoveTo(outer.left,outer.bottom - 3);
  453.             ::LineTo(outer.left,outer.top + 2);
  454.             ::MoveTo(outer.left + 2, outer.top);
  455.             ::LineTo(outer.right - 3,outer.top);
  456.             
  457.             // outside edge, bottom right unshadow
  458.             UGBDraw::ForeGrey(UGBDraw_greyC);
  459.             ::MoveTo(outer.left + 2, outer.bottom - 1);
  460.             ::LineTo(outer.right - 3, outer.bottom - 1);
  461.             ::MoveTo(outer.right - 1, outer.bottom - 3);
  462.             ::LineTo(outer.right - 1, outer.top + 2);
  463.             
  464.             // outside edge, top left corner
  465.             UGBDraw::ForeGrey(UGBDraw_grey5);
  466.             ::MoveTo(outer.left + 1, outer.top + 1);
  467.             ::Line(0,0);
  468.             
  469.             // outside edge, top right corner
  470.             UGBDraw::ForeGrey(UGBDraw_grey5);
  471.             ::MoveTo(outer.right - 2, outer.top + 1);
  472.             ::Line(0,0);
  473.             
  474.             // outside edge, bottom left corner
  475.             UGBDraw::ForeGrey(UGBDraw_grey5);
  476.             ::MoveTo(outer.left + 1, outer.bottom - 2);
  477.             ::Line(0,0);
  478.             
  479.             // outside edge, bottom right corner
  480.             UGBDraw::ForeGrey(UGBDraw_greyC);
  481.             ::MoveTo(outer.right - 2, outer.bottom - 2);
  482.             ::Line(0,0);
  483.         }
  484.         
  485.         {    // inner rim
  486.             Rect    inner = mControl->contrlRect;
  487.             ::InsetRect(&inner,2,2);
  488.             
  489.             // inside edge, top left shadow
  490.             UGBDraw::ForeGrey(UGBDraw_grey5);
  491.             ::MoveTo(inner.left, inner.bottom - 2);
  492.             ::LineTo(inner.left, inner.top + 1);
  493.             ::MoveTo(inner.left + 1, inner.top);
  494.             ::LineTo(inner.right - 2, inner.top);
  495.             
  496.             // inside edge, bottom right unshadow
  497.             UGBDraw::ForeGrey(UGBDraw_greyA);
  498.             ::MoveTo(inner.left + 1, inner.bottom - 1);
  499.             ::LineTo(inner.right - 2, inner.bottom - 1);
  500.             ::MoveTo(inner.right - 1, inner.bottom - 2);
  501.             ::LineTo(inner.right - 1, inner.top + 1);
  502.             
  503.             // inside edge, top left corner
  504.             UGBDraw::ForeGrey(UGBDraw_grey4);
  505.             ::MoveTo(inner.left + 1, inner.top + 1);
  506.             ::Line(0,0);
  507.             
  508.             // inside edge, top right corner
  509.             UGBDraw::ForeGrey(UGBDraw_grey4);
  510.             ::MoveTo(inner.right - 2, inner.top + 1);
  511.             ::Line(0,0);
  512.             
  513.             // inside edge, bottom left corner
  514.             UGBDraw::ForeGrey(UGBDraw_grey4);
  515.             ::MoveTo(inner.left + 1, inner.bottom - 2);
  516.             ::Line(0,0);
  517.             
  518.             // inside edge, bottom right corner
  519.             UGBDraw::ForeGrey(UGBDraw_greyA);
  520.             ::MoveTo(inner.right - 2, inner.bottom - 2);
  521.             ::Line(0,0);
  522.         }
  523.     } else {    // draw unhighlighted
  524.         {    // outer rim
  525.             Rect    outer    = mControl->contrlRect;
  526.             ::InsetRect(&outer,1,1);
  527.  
  528.             // outside edge, bottom left shadow
  529.             UGBDraw::ForeGrey(UGBDraw_grey5);
  530.             ::MoveTo(outer.left + 2,outer.bottom - 1);
  531.             ::LineTo(outer.right - 3,outer.bottom - 1);
  532.             ::MoveTo(outer.right - 1,outer.bottom - 3);
  533.             ::LineTo(outer.right - 1,outer.top + 2);
  534.                         
  535.             // outside edge, top left corner
  536.             UGBDraw::ForeGrey(UGBDraw_greyF);
  537.             ::MoveTo(outer.left + 1, outer.top + 1);
  538.             ::Line(0,0);
  539.             
  540.             // outside edge, top right corner
  541.             UGBDraw::ForeGrey(UGBDraw_greyB);
  542.             ::MoveTo(outer.right - 2, outer.top + 1);
  543.             ::Line(0,0);
  544.             
  545.             // outside edge, bottom left corner
  546.             UGBDraw::ForeGrey(UGBDraw_greyB);
  547.             ::MoveTo(outer.left + 1, outer.bottom - 2);
  548.             ::Line(0,0);
  549.             
  550.             // outside edge, bottom right corner
  551.             UGBDraw::ForeGrey(UGBDraw_grey5);
  552.             ::MoveTo(outer.right - 2, outer.bottom - 2);
  553.             ::Line(0,0);
  554.         }
  555.         
  556.         {    // inner rim
  557.             Rect    inner = mControl->contrlRect;
  558.             ::InsetRect(&inner,2,2);
  559.             
  560.             // inside edge, top left unshadow
  561.             UGBDraw::ForeGrey(UGBDraw_greyF);
  562.             ::MoveTo(inner.left, inner.bottom - 2);
  563.             ::LineTo(inner.left, inner.top + 1);
  564.             ::MoveTo(inner.left + 1, inner.top);
  565.             ::LineTo(inner.right - 2, inner.top);
  566.             
  567.             // inside edge, bottom right shadow
  568.             UGBDraw::ForeGrey(UGBDraw_grey8);
  569.             ::MoveTo(inner.left + 1, inner.bottom - 1);
  570.             ::LineTo(inner.right - 2, inner.bottom - 1);
  571.             ::MoveTo(inner.right - 1, inner.bottom - 2);
  572.             ::LineTo(inner.right - 1, inner.top + 1);
  573.             
  574.             // inside edge, top left corner
  575.             UGBDraw::ForeGrey(UGBDraw_greyF);
  576.             ::MoveTo(inner.left + 1, inner.top + 1);
  577.             ::Line(0,0);
  578.             
  579.             // inside edge, top right corner
  580.             UGBDraw::ForeGrey(UGBDraw_greyB);
  581.             ::MoveTo(inner.right - 2, inner.top + 1);
  582.             ::Line(0,0);
  583.             
  584.             // inside edge, bottom left corner
  585.             UGBDraw::ForeGrey(UGBDraw_greyB);
  586.             ::MoveTo(inner.left + 1, inner.bottom - 2);
  587.             ::Line(0,0);
  588.             
  589.             // inside edge, bottom right corner
  590.             UGBDraw::ForeGrey(UGBDraw_grey8);
  591.             ::MoveTo(inner.right - 2, inner.bottom - 2);
  592.             ::Line(0,0);
  593.         }
  594.     }
  595. }
  596.